using System.Collections.Generic;

namespace System.DataStructures
{
	/// <summary>
	/// 
	/// </summary>
	internal sealed class CharacterSet : SetBase<char>
	{
		private const int CharToPositiveIntMask = 0x0000FFFF;

		private readonly SparsePascalSet _mappedSet;

		/// <summary>
		/// Initializes a new instance of the <see cref="CharacterSet"/> class.
		/// </summary>
		public CharacterSet()
		{
			_mappedSet = new SparsePascalSet();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CharacterSet"/> class.
		/// </summary>
		/// <param name="initialValues">The initial values.</param>
		public CharacterSet(IEnumerable<char> initialValues)
			: this()
		{
			if (initialValues == null)
			{
				return;
			}
			foreach (char value in initialValues)
			{
				Add(value);
			}
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CharacterSet"/> class.
		/// </summary>
		/// <param name="initialValues">The initial values.</param>
		public CharacterSet(params char[] initialValues)
			: this()
		{
			if (initialValues == null)
			{
				return;
			}
			foreach (char value in initialValues)
			{
				Add(value);
			}
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CharacterSet"/> class.
		/// </summary>
		/// <param name="mappedSet">The mapped set.</param>
		private CharacterSet(SparsePascalSet mappedSet)
		{
			_mappedSet = mappedSet;
		}

		/// <summary>
		/// The number of elements currently contained in this collection.
		/// </summary>
		/// <value></value>
		public override int Count
		{
			get { return _mappedSet.Count; }
		}

		/// <summary>
		/// Adds the specified value to the set.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns></returns>
		public override bool Add(char value)
		{
			return _mappedSet.Add(value & CharToPositiveIntMask);
		}

		/// <summary>
		/// Removes all values from the set.
		/// </summary>
		public override void Clear()
		{
			_mappedSet.Clear();
		}

		/// <summary>
		/// Clones the specified trim reserved space.
		/// </summary>
		/// <param name="trimReservedSpace">if set to <c>true</c> [trim reserved space].</param>
		/// <returns></returns>
		public CharacterSet Clone(bool trimReservedSpace = false)
		{
			return new CharacterSet(_mappedSet.Clone(trimReservedSpace));
		}

		public override ISetEx<char> CloneType(IEnumerable<char> initialContents = null)
		{
			return new CharacterSet(initialContents);
		}

		/// <summary>
		/// Compacts the specified reserved block count.
		/// </summary>
		/// <param name="reservedBlockCount">The reserved block count.</param>
		public void Compact(int reservedBlockCount = SparsePascalSet.MinimumReservedBlocks)
		{
			_mappedSet.Compact(reservedBlockCount);
		}

		/// <summary>
		/// Determines whether [contains] [the specified value].
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>
		/// 	<c>true</c> if [contains] [the specified value]; otherwise, <c>false</c>.
		/// </returns>
		public override bool Contains(char value)
		{
			return _mappedSet.Contains(value & CharToPositiveIntMask);
		}

		public override ISetEx<char> Difference(IEnumerable<char> items)
		{
			CharacterSet result = Clone();
			if (!items.IsNullOrEmpty())
			{
				CharacterSet other = items as CharacterSet;
				if (other != null)
				{
					result.DifferenceWith(other);
				}
				else
				{
					result.DifferenceWith(items);
				}
			}
			return result;
		}

		public void DifferenceWith(CharacterSet set)
		{
			if (set.IsNullOrEmpty())
			{
				return;
			}
			_mappedSet.DifferenceWith(set._mappedSet);
		}

		public override void DifferenceWith(IEnumerable<char> items)
		{
			if (items.IsNullOrEmpty())
			{
				return;
			}
			CharacterSet other = items as CharacterSet;
			if (other != null)
			{
				DifferenceWith(other);
			}
			else
			{
				base.DifferenceWith(items);
			}
		}

		/// <summary>
		/// Equalses the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns></returns>
		public override bool Equals(object value)
		{
			if (value != null)
			{
				CharacterSet characterSet = value as CharacterSet;
				if (characterSet != null)
				{
					return _mappedSet.Equals(characterSet._mappedSet);
				}
			}
			return false;
		}

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
		/// </returns>
		public override IEnumerator<char> GetEnumerator()
		{
			foreach (int value in _mappedSet)
			{
				yield return (char)(value & 0x0000FFFF);
			}
		}

		/// <summary>
		/// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"></see> is suitable for use in hashing algorithms and data structures like a hash table.
		/// </summary>
		/// <returns>
		/// A hash code for the current <see cref="T:System.Object"></see>.
		/// </returns>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		public override ISetEx<char> Intersect(IEnumerable<char> items)
		{
			if(items.IsNullOrEmpty())
			{
				return new CharacterSet();
			}
			CharacterSet result = Clone();
			CharacterSet other = items as CharacterSet;
			if (other != null)
			{
				result.IntersectWith(other);
			}
			else
			{
				return base.Intersect(items);
			}
			return result;
		}

		public void IntersectWith(CharacterSet set)
		{
			if (set.IsNullOrEmpty())
			{
				Clear();
				return;
			}
			_mappedSet.IntersectWith(set._mappedSet);
		}

		public override void IntersectWith(IEnumerable<char> items)
		{
			if (items.IsNullOrEmpty())
			{
				Clear();
				return;
			}
			CharacterSet other = items as CharacterSet;
			if (other != null)
			{
				IntersectWith(other);
			}
			else
			{
				base.IntersectWith(items);
			}
		}

		public bool IsSubsetOf(CharacterSet set)
		{
			if (set.IsNullOrEmpty())
			{
				return _mappedSet.IsEmpty;
			}
			return _mappedSet.IsSubsetOf(set._mappedSet);
		}

		public override bool IsSubsetOf(IEnumerable<char> items)
		{
			CharacterSet other = items as CharacterSet;
			return (other != null) ? IsSubsetOf(other) : base.IsSubsetOf(items);
		}

		public bool IsSupersetOf(CharacterSet set)
		{
			return set.IsNullOrEmpty() || _mappedSet.IsSupersetOf(set._mappedSet);
		}

		public override bool IsSupersetOf(IEnumerable<char> items)
		{
			CharacterSet other = items as CharacterSet;
			return (other != null) ? IsSupersetOf(other) : base.IsSupersetOf(items);
		}

		/// <summary>
		/// Removes the specified value from the set if it was there.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns></returns>
		public override bool Remove(char value)
		{
			return _mappedSet.Remove(value & CharToPositiveIntMask);
		}



		public override ISetEx<char> SymmetricDifference(IEnumerable<char> items)
		{
			CharacterSet result = Clone();
			if (!items.IsNullOrEmpty())
			{
				CharacterSet characterSet = items as CharacterSet;
				if (characterSet != null)
				{
					result.SymmetricDifferenceWith(characterSet);
				}
				else
				{
					result.SymmetricDifferenceWith(items);
				}
			}
			return result;
		}

		public void SymmetricDifferenceWith(CharacterSet set)
		{
			if (set.IsNullOrEmpty())
			{
				return;
			}
			_mappedSet.SymmetricDifferenceWith(set._mappedSet);
		}

		public override void SymmetricDifferenceWith(IEnumerable<char> items)
		{
			if (items.IsNullOrEmpty())
			{
				return;
			}
			CharacterSet other = items as CharacterSet;
			if (other != null)
			{
				SymmetricDifferenceWith(other);
			}
			else
			{
				base.SymmetricDifferenceWith(items);
			}
		}

		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString()
		{
			return _mappedSet.ToString();
		}

		public override ISetEx<char> Union(IEnumerable<char> items)
		{
			CharacterSet result = Clone();
			if (!items.IsNullOrEmpty())
			{
				CharacterSet other = items as CharacterSet;
				if (other != null)
				{
					result.UnionWith(other);
				}
				else
				{
					result.UnionWith(items);
				}
			}
			return result;
		}

		public void UnionWith(CharacterSet set)
		{
			if (set.IsNullOrEmpty())
			{
				return;
			}
			_mappedSet.UnionWith(set._mappedSet);
		}

		public override void UnionWith(IEnumerable<char> items)
		{
			if (items.IsNullOrEmpty())
			{
				return;
			}
			CharacterSet other = items as CharacterSet;
			if (other != null)
			{
				UnionWith(other);
			}
			else
			{
				base.UnionWith(items);
			}
		}

		/// <summary>
		/// Implements the operator &amp;.
		/// </summary>
		/// <param name="set1">The set1.</param>
		/// <param name="set2">The set2.</param>
		/// <returns>The result of the operator.</returns>
		public static CharacterSet operator &(CharacterSet set1, CharacterSet set2)
		{
			Guard.NotNull(set1, "set1");
			return (CharacterSet)set1.Intersect(set2);
		}

		/// <summary>
		/// Implements the operator |.
		/// </summary>
		/// <param name="set1">The set1.</param>
		/// <param name="set2">The set2.</param>
		/// <returns>The result of the operator.</returns>
		public static CharacterSet operator |(CharacterSet set1, CharacterSet set2)
		{
			Guard.NotNull(set1, "set1");
			return (CharacterSet)set1.Union(set2);
		}

		/// <summary>
		/// Implements the operator ^.
		/// </summary>
		/// <param name="set1">The set1.</param>
		/// <param name="set2">The set2.</param>
		/// <returns>The result of the operator.</returns>
		public static CharacterSet operator ^(CharacterSet set1, CharacterSet set2)
		{
			Guard.NotNull(set1, "set1");
			return (CharacterSet)set1.SymmetricDifference(set2);
		}

		/// <summary>
		/// Implements the operator -.
		/// </summary>
		/// <param name="set1">The set1.</param>
		/// <param name="set2">The set2.</param>
		/// <returns>The result of the operator.</returns>
		public static CharacterSet operator -(CharacterSet set1, CharacterSet set2)
		{
			Guard.NotNull(set1, "set1");
			return (CharacterSet)set1.Difference(set2);
		}
	}
}